package lumis.portal.serviceinterfaceinstance.serialization;

import lumis.portal.PortalException;
import lumis.portal.PortalRequestParameters;
import lumis.portal.manager.ManagerFactory;
import lumis.portal.manager.ManagerFactoryInternal;
import lumis.portal.page.PageConfig;
import lumis.portal.serialization.BaseSerializationContext;
import lumis.portal.serialization.Dependency;
import lumis.portal.serialization.DeserializationContext;
import lumis.portal.serialization.SerializationContext;
import lumis.portal.serialization.SerializationMessageConfigNode;
import lumis.portal.serialization.SerializationMessageConfigNode.RecursionStopPoints;
import lumis.portal.serialization.operation.ConfigForeignKey;
import lumis.portal.serviceinstance.ServiceInstanceConfig;
import lumis.portal.serviceinterface.ServiceInterfaceConfig;
import lumis.portal.serviceinterface.style.ServiceInterfaceStyleConfig;
import lumis.portal.serviceinterfaceinstance.ServiceInterfaceInstanceConfig;

import java.util.Arrays;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;

/**
 * Manager for Service Interface Instance serialization
 *
 * @version $Revision: 12710 $ $Date: 2011-03-18 17:00:16 -0300 (Fri, 18 Mar 2011) $
 * @since 4.0.7
 */
public class ServiceInterfaceInstanceSerializationManager implements IServiceInterfaceInstanceSerializationManager
{
	private final String[] primaryKeyFieldNames = new String[] { "id" };
	protected final List<ConfigForeignKey> foreignKeys = Arrays.asList(new ConfigForeignKey[] {
		new ConfigForeignKey("serviceInstanceId", null, false, false, true, ManagerFactory.getServiceInstanceManager()),
		new ConfigForeignKey("pageId"),
		new ConfigForeignKey("parentInterfaceInstanceId"),
		new ConfigForeignKey("serviceInterfaceId", null, false, true, false, ManagerFactory.getServiceInterfaceManager()),
		new ConfigForeignKey("serviceInterfaceStyleId", null, false, true, false, ManagerFactory.getServiceInterfaceInstanceManager().getServiceInterfaceInstanceStyleConfigKeyChecker())
	});

	private final String configAlias = "serviceInterfaceInstance";
	private final String customPropertiesAlias = "serviceInterfaceInstanceCustomProperties";

	// DESERIALIZATION

	@SuppressWarnings("unchecked")
	public void deserialize(DeserializationContext deserializationContext, ServiceInterfaceInstanceSerializableConfig serviceInterfaceInstanceSerializableConfig) throws PortalException
	{
		deserializationContext.logDebug("Deserializing service interface instance '" + serviceInterfaceInstanceSerializableConfig.getId() + "'");

		// deserialize config
		ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig = ManagerFactoryInternal.getSerializationManager2().deserializeObject(ServiceInterfaceInstanceConfig.class, deserializationContext.getDirectory(), getConfigPath(deserializationContext, serviceInterfaceInstanceSerializableConfig.getId()), configAlias, false);

		// apply primary key id mappings
		deserializationContext.applyPrimaryKeyFieldIdMappings(serviceInterfaceInstanceConfig, primaryKeyFieldNames);

		// deserialize custom properties
		Map<String, String> customProperties = ManagerFactoryInternal.getSerializationManager2().deserializeObject(Map.class, deserializationContext.getDirectory(), getCustomPropertiesConfigPath(deserializationContext, serviceInterfaceInstanceSerializableConfig.getId()), customPropertiesAlias, true);

		// analyze dependencies and persist config
		ServiceInterfaceInstanceConfigDependencyAnalyzer dependencyAnalyzer = new ServiceInterfaceInstanceConfigDependencyAnalyzer(deserializationContext, foreignKeys, new ServiceInterfaceInstancePersistableConfig(serviceInterfaceInstanceConfig, customProperties));
		dependencyAnalyzer.persist(this, false);
	}

	public boolean persistConfig(DeserializationContext deserializationContext, Object config, boolean firstPersistence) throws PortalException
	{
		boolean persisted = false;

		// apply foreign key id mappings and analyze dependencies
		ServiceInterfaceInstanceConfig mappedServiceInterfaceInstanceConfig = null;
		ServiceInterfaceInstanceConfigDependencyAnalyzer dependencyAnalyzer = new ServiceInterfaceInstanceConfigDependencyAnalyzer(deserializationContext, foreignKeys, (ServiceInterfaceInstancePersistableConfig)config);
		if (dependencyAnalyzer.foreignKeysExists())
		{
			ServiceInterfaceInstancePersistableConfig mappedServiceInterfaceInstancePersistableConfig = dependencyAnalyzer.getMappedServiceInterfaceInstancePersistableConfig();
			mappedServiceInterfaceInstanceConfig = mappedServiceInterfaceInstancePersistableConfig.getServiceInterfaceInstanceConfig();

			// add or update config
			if (deserializationContext.isUpdateIfElementExists() || !firstPersistence)
			{
				deserializationContext.logDebug("Adding/updating service interface instance config:", mappedServiceInterfaceInstanceConfig);
				ManagerFactory.getServiceInterfaceInstanceManager().addOrUpdate(deserializationContext.getSessionConfig(), mappedServiceInterfaceInstanceConfig, deserializationContext.getTransaction());
			}
			else
			{
				deserializationContext.logDebug("Adding service interface instance config:", mappedServiceInterfaceInstanceConfig);
				ManagerFactory.getServiceInterfaceInstanceManager().add(deserializationContext.getSessionConfig(), mappedServiceInterfaceInstanceConfig, deserializationContext.getTransaction());
			}
			// set custom properties
			if (mappedServiceInterfaceInstancePersistableConfig.getCustomProperties() != null)
			{
				deserializationContext.logDebug("Setting service interface instance custom properties:", mappedServiceInterfaceInstancePersistableConfig.getCustomProperties());
				ManagerFactory.getServiceInterfaceInstanceManager().setCustomProperties(deserializationContext.getSessionConfig(), mappedServiceInterfaceInstanceConfig.getId(), mappedServiceInterfaceInstancePersistableConfig.getCustomProperties(), deserializationContext.getTransaction());
			}
			persisted = true;
		}

		// persist dependent configs
		if (persisted)
			deserializationContext.persistDependentConfigs(((ServiceInterfaceInstancePersistableConfig)config).getServiceInterfaceInstanceConfig().getId());

		return persisted;
	}

	// SERIALIZATION

	public ServiceInterfaceInstanceSerializableConfig serialize(SerializationContext serializationContext, String serviceInterfaceInstanceId) throws PortalException
	{
		// get config
		ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig = ManagerFactory.getServiceInterfaceInstanceManager().get(serializationContext.getSessionConfig(), serviceInterfaceInstanceId,
				serializationContext.getTransaction());

		// serialize config
		ManagerFactoryInternal.getSerializationManager2().serializeObject(serviceInterfaceInstanceConfig, serializationContext.getDirectory(), getConfigPath(serializationContext, serviceInterfaceInstanceConfig.getId()), configAlias);

		// serialize custom properties
		Map<String, String> customProperties = ManagerFactory.getServiceInterfaceInstanceManager().getCustomProperties(serializationContext.getSessionConfig(), serviceInterfaceInstanceConfig.getId(), serializationContext.getTransaction());
		if (!customProperties.isEmpty())
			ManagerFactoryInternal.getSerializationManager2().serializeMap(customProperties, serializationContext.getDirectory(), getCustomPropertiesConfigPath(serializationContext, serviceInterfaceInstanceConfig.getId()), customPropertiesAlias);

		// analyze external dependencies
		analyzeExternalDependencies(serializationContext, serviceInterfaceInstanceConfig);

		// register the used service interface style
		ServiceInterfaceStyleConfig styleConfig = ManagerFactory.getServiceInterfaceManager().getStyle(serializationContext.getSessionConfig(),
				serviceInterfaceInstanceConfig.getServiceInterfaceStyleId(), serviceInterfaceInstanceConfig.getServiceInterfaceId(), serializationContext.getTransaction());
		if (styleConfig != null)
			serializationContext.getReferencedServiceInterfaceStyleIds().add(styleConfig.getId() + ";" + styleConfig.getServiceInterfaceId());

		// register the used service and service interface
		ServiceInterfaceConfig serviceInterfaceConfig = ManagerFactory.getServiceInterfaceManager().getServiceInterfaceConfig(serviceInterfaceInstanceConfig.getServiceInterfaceId(), serializationContext.getTransaction());
		serializationContext.addReferencedServiceInterface(serviceInterfaceConfig.getServiceId(), serviceInterfaceConfig.getId());

		// build structure object
		ServiceInterfaceInstanceSerializableConfig serviceInterfaceInstanceSerializableConfig = createSerializableConfig(serviceInterfaceInstanceConfig);

		return serviceInterfaceInstanceSerializableConfig;
	}

	private ServiceInterfaceInstanceSerializableConfig createSerializableConfig(ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig)
	{
		ServiceInterfaceInstanceSerializableConfig serviceInterfaceInstanceSerializableConfig = new ServiceInterfaceInstanceSerializableConfig();
		serviceInterfaceInstanceSerializableConfig.setId(serviceInterfaceInstanceConfig.getId());
		return serviceInterfaceInstanceSerializableConfig;
	}

	private void analyzeExternalDependencies(SerializationContext serializationContext, ServiceInterfaceInstanceConfig dependentServiceInterfaceInstanceConfig) throws PortalException
	{
		// get the dependable service instance's channel id
		String serviceInstanceId = dependentServiceInterfaceInstanceConfig.getServiceInstanceId();
		if (serviceInstanceId != null)
		{
			ServiceInstanceConfig dependableServiceInstanceConfig = ManagerFactory.getServiceInstanceManager().get(serviceInstanceId,
					serializationContext.getTransaction());
			if (dependableServiceInstanceConfig.getChannelId() != null)
			{
				// if the channel is outside the exporting channel tree, treat
				// service instance as an external dependency
				if (!serializationContext.getChannelIds().contains(dependableServiceInstanceConfig.getChannelId()))
				{
					Dependency dependency = new Dependency();
					dependency.setDependentNode(getSerializationMessageConfigNode(serializationContext,
							dependentServiceInterfaceInstanceConfig, EnumSet.of(RecursionStopPoints.ServiceInstance)));
					dependency.setDependableNode(ManagerFactoryInternal.getServiceInstanceSerializationManager().getSerializationMessageConfigNode(
							serializationContext, dependableServiceInstanceConfig));

					serializationContext.getExternalDependencies().add(dependency);
				}
			}
		}
	}

	// GENERAL

	public void setupSerializationContext(BaseSerializationContext baseSerializationContext)
	{
		// define serialization aliases
		baseSerializationContext.getSerializationSettings().getClassAliases().put(ServiceInterfaceInstanceSerializableConfig.class, "serviceInterfaceInstance");
	}

	/**
	 * Get the full path for the serialization file.
	 * @param baseSerializationContext
	 * @param configId
	 * @return
	 */
	private String getConfigPath(BaseSerializationContext baseSerializationContext, String configId)
	{
		return "serviceInterfaceInstances/" + configId + ".xml";
	}

	private String getCustomPropertiesConfigPath(BaseSerializationContext baseSerializationContext, String configId)
	{
		return "serviceInterfaceInstances/" + configId + "_CustomProperties.xml";
	}

	public SerializationMessageConfigNode getSerializationMessageConfigNode(BaseSerializationContext baseSerializationContext, ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig) throws PortalException
	{
		return getSerializationMessageConfigNode(baseSerializationContext, serviceInterfaceInstanceConfig, EnumSet.of(RecursionStopPoints.None));
	}

	public SerializationMessageConfigNode getSerializationMessageConfigNode(BaseSerializationContext baseSerializationContext, ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig, EnumSet<RecursionStopPoints> recursionStopPoints) throws PortalException
	{
		ServiceInterfaceInstanceSerializationMessageConfig serviceInterfaceInstanceSerializationMessageConfig = getServiceInterfaceInstanceSerializationMessageConfig(baseSerializationContext, serviceInterfaceInstanceConfig);

		String pageId = serviceInterfaceInstanceConfig.getPageId();
		PageConfig pageConfig = pageId != null ? ManagerFactory.getPageManager().get(baseSerializationContext.getSessionConfig(), pageId, baseSerializationContext.getTransaction()) : null;
		ServiceInstanceConfig serviceInstanceConfig = ManagerFactory.getServiceInstanceManager().get(serviceInterfaceInstanceConfig.getServiceInstanceId(), baseSerializationContext.getTransaction());
		ServiceInterfaceConfig serviceInterfaceConfig = ManagerFactory.getServiceInterfaceManager().getServiceInterfaceConfig(serviceInterfaceInstanceConfig.getServiceInterfaceId(), baseSerializationContext.getTransaction());

		SerializationMessageConfigNode serializationMessageConfigNode = new SerializationMessageConfigNode();
		serializationMessageConfigNode.setSerializationMessageConfig(serviceInterfaceInstanceSerializationMessageConfig);
		if (!recursionStopPoints.contains(RecursionStopPoints.Page) && pageConfig != null)
			serializationMessageConfigNode.getParentNodes().add(ManagerFactoryInternal.getPageSerializationManager().getSerializationMessageConfigNode(baseSerializationContext, pageConfig, recursionStopPoints));
		if (!recursionStopPoints.contains(RecursionStopPoints.Service))
			serializationMessageConfigNode.getParentNodes().add(ManagerFactoryInternal.getServiceInterfaceSerializationManager().getSerializationMessageConfigNode(baseSerializationContext, serviceInterfaceConfig));
		if (!recursionStopPoints.contains(RecursionStopPoints.ServiceInstance))
			serializationMessageConfigNode.getParentNodes().add(ManagerFactoryInternal.getServiceInstanceSerializationManager().getSerializationMessageConfigNode(baseSerializationContext, serviceInstanceConfig, recursionStopPoints));

		if(pageConfig != null)
			serializationMessageConfigNode.setNavigateScript("window.parent.opener.document.location.href='../../../../"+PageConfig.PAGE_MAIN+"?"+PortalRequestParameters.PAGE_PARAMETER_PAGEID+"=" + pageConfig.getId() + "';if (window.parent.opener.LumisPortalAdmin) window.parent.opener.LumisPortalAdmin.onStructureLayoutClicked(); return false;");

		return serializationMessageConfigNode;
	}

	private ServiceInterfaceInstanceSerializationMessageConfig getServiceInterfaceInstanceSerializationMessageConfig(BaseSerializationContext baseSerializationContext, ServiceInterfaceInstanceConfig serviceInterfaceInstanceConfig) throws PortalException
	{
		ServiceInterfaceInstanceSerializationMessageConfig serviceInterfaceInstanceSerializationMessageConfig = new ServiceInterfaceInstanceSerializationMessageConfig();
		serviceInterfaceInstanceSerializationMessageConfig.setId(serviceInterfaceInstanceConfig.getId());
		serviceInterfaceInstanceSerializationMessageConfig.setTitle(serviceInterfaceInstanceConfig.getTitle());

		return serviceInterfaceInstanceSerializationMessageConfig;
	}
}
